#!/usr/bin/python -S
"""
records.py

Various implementations of objects that can be serialized as JSON.
"""

__author__ = 'Andy Chu'

try:
  import json
except ImportError:
  import simplejson as json


class Record(dict):
  """Will evolve into the glorious Record type.

  Features:
    - Needs to be lazy, like Constants
    - Serialization (what to do about unserializable types?)

  TODO: What is the model for the equivalence of foo-bar and foo_bar?
  Do we store both, but put a facade around?  Or can we only store one?  This is
  causing problems.
  """

  def __getattr__(self, attr):
    try:
      return self[attr]
    except KeyError:
      raise AttributeError(attr)

  def __setattr__(self, attr, value):
    self.__setitem__(attr, value)

  def SetCleanAttr(self, attr, value):
    """Set an attribute that is a Python variable name.

    For example, in-field is converted to in_field, so you can access it like
    record.in_field, rather than record['in-field']
    """
    # TODO: Could validate attribute in other ways.
    self.__setitem__(attr.replace('-', '_'), value)

  def prefix_slice(self, prefix):
    """
    Use under_words style to make the Record look more like a Python built-in.
    """
    sliced = Record()
    prefix = prefix + '.'  # For now, required . to separate items
    for k, v in self.iteritems():
      if k.startswith(prefix):
        sliced[k[len(prefix):]] = v
    return sliced

  def remove_key(self, key):
    """Removes a single key and returns self

    TODO: There should be a variant called without_key that returns a new
    record.  This is basically just an optimization of that function, since in
    the dataflow paradigm we don't reuse old records for anything.  It can be
    more expedient to remove 1 key than to create a new record with N-1 pairs.
    """
    del self[key]
    return self

  def insert_item(self, key, value):
    """Inserts a single item and returns self

    TODO: Variant called with_item()
    """
    self[key] = value
    return self

  def __str__(self):
    # TODO: How to replace JSON implementations?
    return json.dumps(self)

  def ToString(self, format='multiline'):
    """
    Like str(), but with more options.
    """
    if format == 'multiline':
      kwargs = dict(sort_keys=True, indent=2)
    elif format == 'line':
      kwargs = {}
    else:
      raise AssertionError('Invalid format %r' % format)
    return json.dumps(self, **kwargs)

  @staticmethod
  def FromJson(json_str):
    # TODO: error checking
    return Record(**json.loads(json_str))

